import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.*;
import java.util.HashMap;
import java.util.Map;
import java.net.*;
import java.net.UnknownHostException;

public class Client extends UnicastRemoteObject implements Client_itf {


	/**
	 * 
	 */
	private static final long serialVersionUID = 2047379978641529800L;

	static String serverMachineName = "localhost";

	static Client_itf client;
	static Server_itf server;

	static Map<Integer, SharedObject> localObjects;

	public Client() throws RemoteException {
		super();

	}


	///////////////////////////////////////////////////
	//         Interface to be used by applications
	///////////////////////////////////////////////////

	// initialization of the client layer

	public static void init() { 

		String serverURL = "//"+serverMachineName+":"+
			Registry.REGISTRY_PORT+"/SharedObjectServer";
		try {

			Client.server = (Server_itf) Naming.lookup(serverURL);
			Client.client = new Client();

		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}

		localObjects = new HashMap<Integer, SharedObject>();
		System.out.println("Client initialized!");
	}

	// lookup in the name server
	public static SharedObject lookup(String name) { //A LITTLE AMBIGOUS 

		SharedObject so = null;

		try {

			int id = server.lookup(name);
			if(id >= 0)	{
				if(!localObjects.containsKey(id)) {

					Object obj = server.getObject(id);
					so = new SharedObject(id,obj);

					localObjects.put(id, so);
				}else {
					so=localObjects.get(id);
				}

			}

		} catch (RemoteException e) {

			System.out.println("error lookup");

			e.printStackTrace();
		}

		return so;
	}		

	// binding in the name server
	public static void register(String name, SharedObject_itf so) {

		SharedObject res = (SharedObject)so;
		int id = res.id;

		try { 
			server.register(name, id);
		} catch (RemoteException e) {
			e.printStackTrace();
		}

	}

	// creation of a shared object
	public static SharedObject create(Object o) {
		SharedObject so = null;

		try {
			int id = server.create(o);

			so = new SharedObject(id,o);
			localObjects.put(id, so);
		} catch (RemoteException e) {

			System.out.println("error");

			e.printStackTrace();
		}

		return so;
	}

	/////////////////////////////////////////////////////////////
	//    Interface to be used by the consistency protocol
	////////////////////////////////////////////////////////////

	// request a read lock from the server
	public static Object lock_read(int id) {
		Object obj = null;
		try {
			obj = server.lock_read(id, client);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return obj;
	}

	// request a write lock from the server
	public static Object lock_write (int id) {
		Object obj = null;

		try {
			obj = server.lock_write(id, client);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return obj;
	}

	// receive a lock reduction request from the server
	public Object reduce_lock(int id) throws java.rmi.RemoteException {	
		return localObjects.get(id).reduce_lock();
	}


	// receive a reader invalidation request from the server
	public void invalidate_reader(int id) throws java.rmi.RemoteException {
		localObjects.get(id).invalidate_reader();
	}

	// receive a writer invalidation request from the server
	public Object invalidate_writer(int id) throws java.rmi.RemoteException {
		return localObjects.get(id).invalidate_writer();
	}
}
